home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc Development Framework / ODFDev / ODF / OS / FWGraphx / PRTxtBuf.cpp < prev    next >
Encoding:
Text File  |  1996-09-17  |  11.8 KB  |  472 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                PRTxtBuf.h
  4. //    Release Version:    $ ODF 2 $
  5. //
  6. //    Modified by MEB to support non-single-byte characters
  7. //
  8. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  9. //
  10. //========================================================================================
  11.  
  12. #include "FWOS.hpp"
  13.  
  14. #ifndef PRTXTBUF_H
  15. #include "PRTxtBuf.h"
  16. #endif
  17.  
  18. #ifndef FWMEMORY_H
  19. #include "FWMemory.h"
  20. #endif
  21.  
  22. #ifndef FWSOMENV_H
  23. #include "FWSOMEnv.h"
  24. #endif
  25.  
  26. //========================================================================================
  27. //    RunTime Info
  28. //========================================================================================
  29.  
  30. #ifdef FW_BUILD_MAC
  31. #pragma segment FWGraphx_TextBuf
  32. #endif
  33.  
  34. FW_DEFINE_AUTO(FW_CPrivTextBuffer)
  35.  
  36. //========================================================================================
  37. // CLASS FW_CPrivTextBuffer
  38. //========================================================================================
  39.  
  40. //----------------------------------------------------------------------------------------
  41. // FW_CPrivTextBuffer::FW_CPrivTextBuffer
  42. //----------------------------------------------------------------------------------------
  43.  
  44. FW_CPrivTextBuffer::FW_CPrivTextBuffer(FW_HString string) :
  45.     fString(string),
  46.     fReader(NULL),
  47.     fCurrentLine(NULL),
  48.     fCurRun(NULL),
  49.     fCurRunIsLast(FALSE),
  50.     fIsDone(FALSE),
  51.     fIsLastLine(FALSE),
  52.     fDynamicBufferBase(NULL),
  53.     fUsingFixedBuffer(TRUE)
  54. {
  55.     Advance();
  56.     FW_END_CONSTRUCTOR
  57. }
  58.     
  59. //----------------------------------------------------------------------------------------
  60. // FW_CPrivTextBuffer::FW_CPrivTextBuffer
  61. //----------------------------------------------------------------------------------------
  62.  
  63. FW_CPrivTextBuffer::FW_CPrivTextBuffer(FW_HTextReader reader) :
  64.     fString(NULL),
  65.     fReader(reader),
  66.     fCurrentLine(NULL),
  67.     fCurRun(NULL),
  68.     fCurRunIsLast(NULL),
  69.     fIsDone(FALSE),
  70.     fIsLastLine(FALSE),
  71.     fUsingFixedBuffer(TRUE),
  72.     fDynamicBufferBase(NULL)
  73. {
  74.     Advance();
  75.     FW_END_CONSTRUCTOR
  76. }
  77.  
  78. //----------------------------------------------------------------------------------------
  79. // FW_CPrivTextBuffer::~FW_CPrivTextBuffer
  80. //----------------------------------------------------------------------------------------
  81.  
  82. FW_CPrivTextBuffer::~FW_CPrivTextBuffer()
  83. {
  84.     FW_START_DESTRUCTOR
  85.     FW_CMemoryManager::FreeBlock(fDynamicBufferBase);
  86. }
  87.  
  88. //----------------------------------------------------------------------------------------
  89. // FW_CPrivTextBuffer::IsDone
  90. //----------------------------------------------------------------------------------------
  91.  
  92. FW_Boolean FW_CPrivTextBuffer::IsDone() const
  93. {
  94.     return fIsDone;
  95. }
  96.  
  97. //----------------------------------------------------------------------------------------
  98. // FW_CPrivTextBuffer::Advance
  99. //----------------------------------------------------------------------------------------
  100.  
  101. void FW_CPrivTextBuffer::Advance()
  102. {
  103.     // the user should not call this method after we told hir we're done
  104.     FW_ASSERT(!fIsDone);
  105.  
  106.     // get the next run if we need to
  107.     if (fCurRun == NULL)
  108.         if (!GetNextRun())
  109.         {
  110.             fIsDone = TRUE;
  111.             return;
  112.         }
  113.  
  114.     // search for newline in the current run
  115.     char* p = FindRunNewLine();
  116.  
  117.     if (p != NULL)
  118.     {
  119.         // Found a newline
  120.         fCurrentLine         =     fCurRunPos;
  121.         fCurrentLineLength    =     p - fCurRunPos + 1;
  122.         fCurRunPos            =    p + 1;
  123.         return;
  124.     }
  125.  
  126.     if (fCurRunIsLast)
  127.     {
  128.         // No newline, last run - use the remainder of the run
  129.         fCurrentLine        =     fCurRunPos;
  130.         fCurrentLineLength    =     fCurRunLen - (fCurRunPos - fCurRun);
  131.         fIsLastLine            =    TRUE;
  132.         fIsDone                =     fCurrentLineLength == 0;
  133.         return;
  134.     }
  135.  
  136.     // No newline, not the last run - consume the run
  137.     ResetBuffer(fCurRunLen - (fCurRunPos - fCurRun));
  138.     AppendToBuffer(fCurRunPos, fCurRunLen - (fCurRunPos - fCurRun));
  139.  
  140.     // Keep going through the runs until we find a newline or the show is over
  141.     while (GetNextRun())
  142.     {
  143.         char* p = FindRunNewLine();
  144.         FW_ByteCount len = p == NULL ? fCurRunLen : p - fCurRunPos + 1;
  145.  
  146.         AppendToBuffer(fCurRunPos, len);
  147.         fCurRunPos += len;
  148.  
  149.         if (p != NULL)        // Found a newline
  150.             break;
  151.  
  152.         if (fCurRunIsLast)    // This was the last run, bail out
  153.         {
  154.             fIsLastLine = TRUE;
  155.             break;
  156.         }
  157.     }
  158. }
  159.  
  160. //----------------------------------------------------------------------------------------
  161. // FW_CPrivTextBuffer::FindRunNewLine
  162. //----------------------------------------------------------------------------------------
  163.  
  164. char*    FW_CPrivTextBuffer::FindRunNewLine()
  165. {
  166.     char* runLimit = fCurRun + fCurRunLen, *p = fCurRunPos;
  167.     while (p < runLimit && *p != 0x0D)
  168.         ++ p;
  169.  
  170.     return (p < runLimit) ? p : NULL;
  171. }
  172.  
  173. //----------------------------------------------------------------------------------------
  174. // FW_CPrivTextBuffer::GetCurrentLine
  175. //----------------------------------------------------------------------------------------
  176.  
  177. void FW_CPrivTextBuffer::GetCurrentLine(const char*& pLine, FW_ByteCount& len)
  178. {
  179.     pLine    =    fCurrentLine;
  180.     len        =    fCurrentLineLength;
  181.     
  182.     if (fIsLastLine)
  183.         fIsDone = TRUE;
  184. }
  185.  
  186. //----------------------------------------------------------------------------------------
  187. // FW_CPrivTextBuffer::ResetBuffer
  188. //----------------------------------------------------------------------------------------
  189.  
  190. void FW_CPrivTextBuffer::ResetBuffer(FW_ByteCount size)
  191. {
  192.     if(size <= kBufferSize)
  193.     {
  194.         // the fixed buffer is large enough; use it
  195.         FW_CMemoryManager::FreeBlock(fDynamicBufferBase);
  196.         fDynamicBufferBase        = NULL;
  197.         fCurrentBufferBase        = fFixedBuffer;
  198.         fCurrentBufferSize        = kBufferSize;
  199.     }
  200.     else
  201.     {
  202.         // the fixed buffer is too small; allocate a dynamic buffer
  203.         fDynamicBufferBase        = (char*) FW_CMemoryManager::AllocateBlock(kBufferSize);
  204.         fDynamicBufferSize        = kBufferSize;
  205.         fCurrentBufferBase        = fDynamicBufferBase;
  206.         fCurrentBufferSize        = fDynamicBufferSize;
  207.     }
  208.  
  209.     fCurrentLineLength    = 0;
  210.     fCurrentLine        = fCurrentBufferBase;
  211. }
  212.  
  213. //----------------------------------------------------------------------------------------
  214. // FW_CPrivTextBuffer::AppendToBuffer
  215. //----------------------------------------------------------------------------------------
  216.  
  217. void FW_CPrivTextBuffer::AppendToBuffer(const char* pch, FW_ByteCount cb)
  218. {
  219.     if(fCurrentLineLength + cb <= fCurrentBufferSize)
  220.     {
  221.         // the buffer is still large enough, just stick the character in
  222.         FW_PrimitiveCopyMemory(pch, fCurrentLine + fCurrentLineLength, cb);
  223.     }
  224.     else
  225.     {
  226.         // the buffer became too small
  227.         if(fDynamicBufferBase == NULL)
  228.         {
  229.             // we are switching from a static to a dynamic buffer
  230.             fDynamicBufferBase        = (char*) FW_CMemoryManager::AllocateBlock(2 * kBufferSize);
  231.             fDynamicBufferSize        = 2 * kBufferSize;
  232.             fCurrentBufferBase        = fDynamicBufferBase;
  233.             fCurrentBufferSize        = fDynamicBufferSize;
  234.             
  235.             // copy the current contents into the dynamic buffer
  236.             ::FW_PrimitiveCopyMemory(fFixedBuffer, fDynamicBufferBase, kBufferSize);
  237.         }
  238.         else
  239.         {
  240.             // just reallocate the dynamic buffer
  241.             fDynamicBufferSize        += kBufferSize;
  242.             fDynamicBufferBase        = (char*) FW_CMemoryManager::ResizeBlock(fDynamicBufferBase, fDynamicBufferSize);
  243.             fCurrentBufferBase        = fDynamicBufferBase;
  244.             fCurrentBufferSize        = fDynamicBufferSize;
  245.         }
  246.     }
  247.  
  248.     fCurrentLine        = fCurrentBufferBase;
  249.     fCurrentLineLength += cb;
  250. }
  251.  
  252. //----------------------------------------------------------------------------------------
  253. // FW_CPrivTextBuffer::GetNextRun
  254. //----------------------------------------------------------------------------------------
  255.  
  256. FW_Boolean FW_CPrivTextBuffer::GetNextRun()
  257. {
  258.     FW_ASSERT(!fCurRunIsLast);        // No advancing beyond last run!
  259.     if (fString != NULL)
  260.     {
  261.         fCurRun            = (char*)FW_PrivString_RevealBuffer(fString);
  262.         fCurRunLen        = FW_PrivString_GetByteLength(fString);
  263.         fCurRunIsLast    = TRUE;
  264.     }
  265.     else
  266.     {
  267.         FW_PrivTextReader_PeekRunAhead(fReader, (const char**) &fCurRun, &fCurRunLen);
  268.  
  269.         FW_ASSERT(fCurRun != NULL);
  270.         FW_ASSERT(fCurRunLen != 0);
  271.  
  272.         //???JEL: I added the FW_SOMEnvironment and FW_FailOnEvError, but I'm not
  273.         // certain this is a complete error handling stategy for this function.
  274.         FW_SOMEnvironment ev;
  275.         FW_PrivTextReader_Advance(fReader, ev, fCurRunLen);
  276.         FW_FailOnEvError(ev);
  277.  
  278.         fCurRunIsLast = FW_PrivTextReader_GetPosition(fReader)
  279.             >= FW_PrivTextReader_GetByteLength(fReader);
  280.     }
  281.  
  282.     fCurRunPos = fCurRun;
  283.     return fCurRunLen != 0;
  284. }
  285.  
  286. //========================================================================================
  287. // Global Method FW_PrivGetStringSegment
  288. //========================================================================================
  289. //
  290. //      Used by word-wrapping text measurement and drawing methods
  291. //
  292. //      str         - the string we're wrapping
  293. //      len         - length of the string
  294. //      segStart    - start of the segment that should be drawn
  295. //      segLen      - length of the segement that should be drawn
  296. //      maxWidth    - max width that the string can be when drawn
  297. //      actualWidth - the actual width of the string
  298. //      wordWrap    - should we try to wrap text or just draw it in one piece
  299. //        wordBreak   - if the first word doesn't fit, should we break it?
  300.  
  301. FW_Boolean    FW_PrivGetStringSegment(
  302.     ODPlatformCanvas        platformCanvas,
  303.     FW_Boolean&                bCalledBefore,
  304.     const char*                str,
  305.     FW_ByteCount            len,
  306.     FW_BytePosition&        segStart,
  307.     FW_ByteCount&            segLen,
  308.     FW_PlatformCoordinate    maxWidth,
  309.     FW_PlatformCoordinate&    actualWidth,
  310.     FW_Boolean                wordWrap,
  311.     FW_Boolean                wordBreak)
  312. {
  313.     enum
  314.     {
  315.         kSpace = ' ',
  316.         kCR = 0x0D,
  317.         kLF = 0x0A
  318.     };
  319.  
  320.     segStart = 0;
  321.     segLen = 0;
  322.  
  323.     // if we encountered an end-line during the previous call, "str" will point to a newline char
  324.     if (bCalledBefore)
  325.     {
  326.         if (*str == kCR && len > 0)
  327.         {
  328.             str++;
  329.             segStart++;
  330.             len--;
  331.         }
  332.         if (*str == kLF && len > 0)
  333.         {
  334.             str++;
  335.             segStart++;
  336.             len--;
  337.         }
  338.     }
  339.     bCalledBefore = TRUE;
  340.  
  341.     // skip the leading spaces
  342.     while (*str == kSpace && len > 0)
  343.     {
  344.         str++;
  345.         segStart++;
  346.         len--;
  347.     }
  348.  
  349.     if (len == 0)
  350.         return FALSE;
  351.  
  352. #ifdef FW_BUILD_WIN
  353.     SIZE size;
  354. #endif
  355.  
  356.     if (!wordWrap)
  357.     {
  358.         // return the whole string in one chunk
  359.         segLen = len;
  360. #ifdef FW_BUILD_WIN
  361.         ::GetTextExtentPoint(platformCanvas, str, len, &size);
  362.         actualWidth = size.cx;
  363. #endif
  364. #ifdef FW_BUILD_MAC
  365. FW_UNUSED(platformCanvas);
  366.         actualWidth = ::TextWidth(Ptr(str), 0, len);
  367. #endif
  368.     }
  369.     else
  370.     {
  371.         // break the string into chunks
  372.         FW_PlatformCoordinate accumWidth = 0;
  373.         FW_PlatformCoordinate newWidth;
  374.         FW_PlatformCoordinate wordWidth;
  375.         const char* s = str;
  376.         const char* wordEnd = NULL;
  377.  
  378.         segLen = 0;
  379.  
  380.         while (len > 0)
  381.         {
  382.             // check if we are at the end of a word
  383.             if (*s == kSpace)
  384.             {
  385.                 if (*(s - 1) != kSpace)
  386.                 {
  387.                     wordEnd = s;
  388.                     wordWidth = accumWidth;
  389.                 }
  390.             }
  391.  
  392.             // check if we have a line terminator
  393.             if (*s == kCR || *s == kLF)
  394.             {
  395.                 wordEnd = s;
  396.                 wordWidth = accumWidth;
  397.                 break;
  398.             }
  399.  
  400.             // add the current char width
  401. #ifdef FW_BUILD_WIN
  402.             ::GetTextExtentPoint(platformCanvas, str, s - str + 1, &size);
  403.             newWidth = size.cx;
  404. #endif
  405. #ifdef FW_BUILD_MAC
  406.             newWidth = ::TextWidth(Ptr(str), 0, s - str + 1);
  407. #endif
  408.             if (newWidth > maxWidth)
  409.                 break;
  410.  
  411.             accumWidth = newWidth;
  412.  
  413.             // advance to the next character
  414.             s++;
  415.             len--;
  416.         }
  417.  
  418.         // the whole string fit just fine
  419.         if (len == 0)
  420.         {
  421.             wordEnd = s;
  422.             wordWidth = accumWidth;
  423.         }
  424.  
  425.         // the first character was too wide
  426.         if (s == str)
  427.         {
  428.             // ... but it was the first character, so draw it anyways!
  429.             s++;
  430.             len--;
  431. #ifdef FW_BUILD_WIN
  432.             ::GetTextExtentPoint(platformCanvas, str, 1, &size);
  433.             newWidth = size.cx;
  434. #endif
  435.  
  436. #ifdef FW_BUILD_MAC
  437.             newWidth = ::TextWidth(Ptr(str), 0, 1);
  438. #endif
  439.         }
  440.  
  441.         // the first word didn't fit...
  442.         if (wordEnd == NULL)
  443.         {
  444.             if (!wordBreak)
  445.             {
  446.                 // it's not ok to break the first word, keep looking
  447.                 while (len > 0 && *s != ' ')
  448.                 {
  449. #ifdef FW_BUILD_WIN
  450.                     SIZE size;
  451.                     ::GetTextExtentPoint(platformCanvas, str, s - str + 1, &size);
  452.                     accumWidth = size.cx;
  453. #endif
  454. #ifdef FW_BUILD_MAC
  455.                     accumWidth = ::TextWidth(Ptr(str), 0, s - str + 1);
  456. #endif
  457.                     s++;
  458.                     len--;
  459.                 }
  460.             }
  461.  
  462.             wordWidth = accumWidth;
  463.             wordEnd = s;
  464.         }
  465.  
  466.         segLen = wordEnd - str;
  467.         actualWidth = wordWidth;
  468.     }
  469.  
  470.     return TRUE;
  471. }
  472.